#include "mbed.h"

#include "EwWindow.h"

#include "GUI.h"
#include "WM.h"

static void _callback(WM_MESSAGE* pMsg);
static void _register(GUI_HWIN hWin, EwWindow* win, void (*callbackFunc)(WM_MESSAGE* pMsg, EwWindow* w));
static void _deregister(GUI_HWIN hWin);

#define EW_MAX_NUM_WINDOWS (100)

typedef void (*callbackFunc)(WM_MESSAGE* pMsg, EwWindow* w);

static GUI_HWIN _regHandles[EW_MAX_NUM_WINDOWS] = {0};
static EwWindow* _regWindows[EW_MAX_NUM_WINDOWS] = {0};
static callbackFunc _regCallbacks[EW_MAX_NUM_WINDOWS] = {0};


int EwWindow::_guiWidgetId = GUI_ID_USER;

EwWindow::EwWindow(EwWindow* parent) {
    init(0, 0, 0, 0, parent);
}

EwWindow::EwWindow(int x, int y, int width, int height, EwWindow* parent) {
    init(x, y, width, height, parent);
}

EwWindow::~EwWindow() {

    _deregister(_hWnd);

    WM_DeleteWindow(_hWnd);
}

void EwWindow::setParent(EwWindow* parent) {
    WM_HWIN hParent = 0;
    if (parent != NULL) {
        hParent = parent->_hWnd;
    }

    WM_AttachWindow(_hWnd, hParent);
}

void EwWindow::bringToBottom() {
    WM_BringToBottom(_hWnd);
}

void EwWindow::bringToTop() {
    WM_BringToTop(_hWnd);
}

void EwWindow::disable() {
    WM_DisableWindow(_hWnd);
}

void EwWindow::enable() {
    WM_EnableWindow(_hWnd);
}

bool EwWindow::hasTransparency() {
    return (WM_GetHasTrans(_hWnd) == 1);
}

int EwWindow::getX() {
    return WM_GetWindowOrgX(_hWnd);
}

int EwWindow::getY() {
    return WM_GetWindowOrgY(_hWnd);
}

int EwWindow::getWidth() {
    return WM_GetWindowSizeX(_hWnd);
}

int EwWindow::getHeight() {
    return WM_GetWindowSizeY(_hWnd);
}

bool EwWindow::hasInput() {
    return (WM_HasCaptured(_hWnd) == 1);
}

bool EwWindow::hasFocus() {
    return (WM_HasFocus(_hWnd) == 1);
}

bool EwWindow::getStayOnTop() {
    return (WM_GetStayOnTop(_hWnd) == 1);
}

void EwWindow::hide() {
    WM_HideWindow(_hWnd);
}

void EwWindow::invalidate() {
    WM_InvalidateWindow(_hWnd);
}

bool EwWindow::isCompletelyCovered() {
    return (WM_IsCompletelyCovered(_hWnd) == 1);
}

bool EwWindow::isCompletelyVisible() {
    return (WM_IsCompletelyVisible(_hWnd) == 1);
}

bool EwWindow::isEnabled() {
    return (WM_IsEnabled(_hWnd) == 1);
}

bool EwWindow::isVisible() {
    return (WM_IsVisible(_hWnd) == 1);
}

void EwWindow::makeModal() {
    WM_MakeModal(_hWnd);
}

void EwWindow::moveTo(int x, int y) {
    WM_MoveChildTo(_hWnd, x, y);
}

void EwWindow::moveToAbs(int x, int y) {
    WM_MoveTo(_hWnd, x, y);
}

void EwWindow::move(int dx, int dy) {
    WM_MoveWindow(_hWnd, dx, dy);
}

void EwWindow::repaint() {
    WM_Paint(_hWnd);
}

void EwWindow::repaintAll() {
    WM_PaintWindowAndDescs(_hWnd);
}

void EwWindow::resize(int dx, int dy) {
    WM_ResizeWindow(_hWnd, dx, dy);
}

void EwWindow::resizeTo(int width, int height) {
    WM_SetSize(_hWnd, width, height);
}

void EwWindow::setHasTransparency(bool has) {
    if (has) {
        WM_SetHasTrans(_hWnd);
    } else {
        WM_ClrHasTrans(_hWnd);
    }
}

void EwWindow::setStayOnTop(bool onTop) {
    WM_SetStayOnTop(_hWnd, (onTop ? 1 : 0));
}

void EwWindow::show() {
    WM_ShowWindow(_hWnd);
}

void EwWindow::setFocus() {
    WM_SetFocus(_hWnd);
}

void EwWindow::update() {
    WM_Update(_hWnd);
}

void EwWindow::updateAll() {
    WM_UpdateWindowAndDescs(_hWnd);
}

ewColor_t EwWindow::setDesktopColor(ewColor_t c) {
    return WM_SetDesktopColor(c);
}

bool EwWindow::paintEvent() {
    return false;
}

bool EwWindow::touchEvent(int x, int y, EwTouchState_t state) {
    return false;
}

bool EwWindow::focusEvent(bool gotFocus) {
    return false;
}

bool EwWindow::resizedEvent() {
    return false;
}

void EwWindow::_setNewHandle(GUI_HWIN hWnd, void (*callbackFunc)(WM_MESSAGE* pMsg, EwWindow* w)) {
    _deregister(_hWnd);
    WM_DeleteWindow(_hWnd);

    _hWnd = hWnd;
    _register(_hWnd, this, callbackFunc);
}

GUI_HWIN EwWindow::_getHandle(EwWindow* w) {
    GUI_HWIN h = 0;
    WM_CALLBACK *pCb;

    if (w) {
        h = w->_hWnd;
        pCb = WM_GetCallback(h);

        // If the windows is a frame window return its client window instead
        // since the main window handle shouldn't be used when adding child
        // windows
        if (pCb == FRAMEWIN_Callback) {
            h = WM_GetClientWindow(h);
        }

    }

    return h;
}

void EwWindow::init(int x, int y, int width, int height, EwWindow* parent) {

    // TODO: flags

    if (parent) {
        _hWnd = WM_CreateWindowAsChild(x, y, width, height, _getHandle(parent),
                (WM_CF_SHOW | WM_CF_MEMDEV), _callback, 0);
    } else {
        _hWnd = WM_CreateWindow(x, y, width, height, (WM_CF_SHOW | WM_CF_MEMDEV),
                _callback, 0);
    }

    _register(_hWnd, this, NULL);
}

static void _callback(WM_MESSAGE* pMsg) {

    GUI_PID_STATE* pState;
    EwTouchState_t touchState;

    bool msgHandled = false;


    for (int i = 0; i < EW_MAX_NUM_WINDOWS; i++) {
        if (_regHandles[i] == pMsg->hWin) {

            // callback is overriden
            if (_regCallbacks[i] != NULL) {
                _regCallbacks[i](pMsg, _regWindows[i]);

                // it is the responsibility of the overriden callback function
                // to handle all messages or forward those that aren't handled
                // to default handler.
                msgHandled = false;
            }

            else {

                switch (pMsg->MsgId) {
                case WM_PAINT:
                    msgHandled = _regWindows[i]->paintEvent();
                    break;
                case WM_TOUCH:
                    pState = (GUI_PID_STATE*)pMsg->Data.p;
                    if (pState->Pressed == 1) {
                        touchState = TouchStatePressed;
                    }
                    else if (pState->Pressed == 0) {
                        touchState = TouchStateReleased;
                    }
                    else {
                        touchState = TouchStateReleasedOutside;
                    }

                    msgHandled = _regWindows[i]->touchEvent(pState->x, pState->y, touchState);

                    break;
                case WM_SET_FOCUS:
                    msgHandled = _regWindows[i]->focusEvent(pMsg->Data.v == 1);
                    break;
                case WM_SIZE:
                    msgHandled = _regWindows[i]->resizedEvent();
                    break;

                default:
                    msgHandled = false;
                }


            }



            break;
        }
    }

    if (!msgHandled) {
        WM_DefaultProc(pMsg);
    }
}

static void _register(GUI_HWIN hWin, EwWindow* win, void (*callbackFunc)(WM_MESSAGE* pMsg, EwWindow* w)) {
    for (int i = 0; i < EW_MAX_NUM_WINDOWS; i++) {
        if (_regWindows[i] == NULL) {
            _regWindows[i] = win;
            _regHandles[i] = hWin;
            _regCallbacks[i] = callbackFunc;

            if (callbackFunc != NULL) {
                WM_SetCallback(hWin, _callback);
            }

            break;
        }
    }
}

static void _deregister(GUI_HWIN hWin) {

    for (int i = 0; i < EW_MAX_NUM_WINDOWS; i++) {
        if (_regHandles[i] == hWin) {
            _regWindows[i] = NULL;
            _regHandles[i] = 0;
            _regCallbacks[i] = NULL;
            break;
        }
    }
}

